package jesss.commands;

import com.amazonaws.AmazonClientException;
import com.amazonaws.services.s3.model.*;
import jesss.options.ExtendedGnuParser;
import jesss.options.JesssOptions;
import org.apache.commons.cli.*;

import java.util.ArrayList;
import java.util.List;

/**
 * User: Lee
 * Date: 8/19/13
 * Time: 7:13 PM
 */
public class ListObjectsCommand<T> extends AbstractJesssCommand {

    private final int DEFAULT_MAX_KEYS = 1000;
    private JesssOptions jesssOptions = null;
    private Options options = new Options();

    private String bucketName;
    private String prefix;
    private String delimiter;
    private int maxKeys = 0;

    public ListObjectsCommand(JesssOptions jesssOptions) throws Exception {
        this.jesssOptions = jesssOptions;
        setOptions();
        try {
            parseArgs(jesssOptions.getArgs());
        } catch (Exception e) {
            printUsage();
            throw e;
        }
    }

    /**
     * The ListObjects command will print the returned S3ObjectSummary information to System.out as well
     * as returning the list of S3ObjectSummary to the caller.
     *
     * @return The CommandResult will contain a list of S3ObjectSummary if the ResultStatus is Success.
     *         Otherwise the CommandResult will have a message with error information.
     */
    @Override
    public CommandResult<List<S3ObjectSummary>> executeCommand() {

        CommandResult<List<S3ObjectSummary>> commandResult = new CommandResult<>();

        try {
            List<S3ObjectSummary> s3ObjectSummaries = new ArrayList<>();
            ListObjectsRequest listObjectsRequest = getListObjectsRequest();

            ObjectListing objectListing;


            do {
                objectListing = execute(listObjectsRequest);
                if (objectListing != null)  {
                    s3ObjectSummaries.addAll(objectListing.getObjectSummaries());
                    String nextMarker = objectListing.getNextMarker();
                    if (nextMarker != null && !nextMarker.isEmpty()) {
                        listObjectsRequest.setMarker(nextMarker);
                    }
                }
            } while(objectListing != null && listObjectsRequest.getMaxKeys() == objectListing.getObjectSummaries().size());

            commandResult.setResultStatus(ResultStatus.Success);
            commandResult.setResultValue(s3ObjectSummaries);

        } catch (AmazonClientException e) {
            commandResult.setResultStatus(ResultStatus.Error );
            commandResult.setStatusMessage("Unable to list objects.");
            addExceptionInfoToCommandResult(commandResult, e);
        }

        return commandResult;
    }

    private ObjectListing execute(ListObjectsRequest listObjectsRequest) {
        List<S3ObjectSummary> s3ObjectSummaries = null;
        ObjectListing objectListing = jesssOptions.getAmazonS3().listObjects(listObjectsRequest);
        if (objectListing != null) {
            s3ObjectSummaries = objectListing.getObjectSummaries();
            for (S3ObjectSummary object : s3ObjectSummaries) {
                System.out.printf("%s\t%s\t%s\t%s\t%s\t%s%n", object.getBucketName(), object.getKey(), object.getSize(), object.getStorageClass(), object.getETag(), object.getLastModified());
            }
        }

        return objectListing;
    }

    private ListObjectsRequest getListObjectsRequest() {

        ListObjectsRequest listObjectRequest = new ListObjectsRequest().withBucketName(bucketName);

        if (delimiter != null && !delimiter.isEmpty()) {
            listObjectRequest.withDelimiter(delimiter);
        }

        if (maxKeys > 0) {
            listObjectRequest.withMaxKeys(maxKeys);
        }

        if (prefix != null && !prefix.isEmpty()) {
            listObjectRequest.withPrefix(prefix);
        }

        return listObjectRequest;
    }


    private void setOptions() {
        options = new Options();
        options.addOption("bn", "bucketName", true, "Bucket name to list objects from");
        options.addOption("pre", "prefix", true, "Key prefix");
        options.addOption("d", "delimiter", true, "Delimiter");
        options.addOption("mk", "maxKeys", true, "Max keys");
    }

    private void parseArgs(String[] args) throws Exception {
        if (args == null || args.length == 0) {
            printUsage();
        }

        try {
            CommandLineParser parser = new ExtendedGnuParser(true);
            CommandLine cmd = parser.parse(options, args);

            if (cmd.hasOption("bn")) {
                bucketName = cmd.getOptionValue("bn");
            } else {
                throw new MissingArgumentException("Missing bucket name parameter.");
            }

            if (cmd.hasOption("pre")) {
                prefix = cmd.getOptionValue("pre");
            }

            if (cmd.hasOption("d")) {
                delimiter = cmd.getOptionValue("d");
            }

            if (cmd.hasOption("mk")) {
                maxKeys = Integer.parseInt(cmd.getOptionValue("mk"));
            } else {
                maxKeys = DEFAULT_MAX_KEYS;
            }
        } catch (Exception e) {
            printUsage();
            throw e;
        }
    }

    private void printUsage() {
        System.out.println("Usage:");
        System.out.println("jesss.jesss -cn listObjects -bn <mybucket> [-p <prefix> -d <delimiter> -mk <max keys>]");
    }
}
